/*

author: YiliangWu
杂项：
)id生成器
)WeakPtr

Twitter's SnowFlake算法
最高位是符号位，始终为0，不可用。
41位的时间序列，精确到毫秒级，41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
16位的节点标识，10位的长度最多支持部署65,536个节点。(8位数据中心标识 + 8位机器标识)
6位的计数序列号，序列号即一系列的自增id，可以支持同一节点同一毫秒生成多个ID序号，6位的计数序列号支持每个节点每毫秒产生64个ID序号。

缺点： 存在时钟回拨问题， 时钟回拨 会有概率导致重复id.  
*/

#pragma once

#include <vector>
#include <list>
#include <set>
#include <time.h>
#include "typedef.h"
#include <functional>
#include <memory>
#include <chrono>
#include <stdexcept>

#define UNUSED(x) (void)(x)  //无用代码，用来去编译警告

namespace su
{

	//@Twepoch 表示开始时间戳。 毫秒
	//1664932906 == 2022年 10月 05日 星期三 09:21:46 CST
	template<int64_t Twepoch = 1664932906000L>
	class snowflake
	{
		static constexpr int64_t TWEPOCH = Twepoch;
		static constexpr int64_t WORKER_ID_BITS = 8L;// x位机器标识 
		static constexpr int64_t DATACENTER_ID_BITS = 8L;//x位数据中心标识
		static constexpr int64_t SEQUENCE_BITS = 6L; //x位的计数序列号
		static constexpr int64_t MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1;
		static constexpr int64_t MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) - 1;
		static constexpr int64_t WORKER_ID_SHIFT = SEQUENCE_BITS;
		static constexpr int64_t DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
		static constexpr int64_t TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
		static constexpr int64_t SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;

		using time_point = std::chrono::time_point<std::chrono::steady_clock>;

		//steady_clock这个时钟和system_clock不同，system_clock与系统时间相对应，如果手工调整或者操作系统自动与网络时钟同步时调用system_clock.now()获得的时间也会收到影响，两次调用时，可能第二次调用时获取的时间反而更早。steady_clock保证时间是匀速前进的，不受系统时钟调整的影响。
		time_point start_time_point_ = std::chrono::steady_clock::now();
		//当前时间戳 毫秒
		// std::chrono::system_clock::now().time_since_epoch().count() 表示从纪元开始到当前时间， 单位微秒。 //注：计算机时间纪元是1970年1月1日 00:00:00
		//std::chrono::duration_cast<std::chrono::milliseconds>(microsecond).count() 表示微秒转换为毫秒
		int64_t start_millsecond_ = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

		int64_t last_timestamp_ = -1;
		int64_t workerid_ = 0;
		int64_t datacenterid_ = 0;
		int64_t sequence_ = 0;
	public:
		snowflake() = default;

		snowflake(const snowflake&) = delete;

		snowflake& operator=(const snowflake&) = delete;

		//@workerid 进程id < 255
		//@datacenterid 数据中心id < 255
		void init(int64_t workerid, int64_t datacenterid)
		{
			if (workerid > MAX_WORKER_ID || workerid < 0) {
				throw std::runtime_error("worker Id can't be greater than 31 or less than 0");
			}

			if (datacenterid > MAX_DATACENTER_ID || datacenterid < 0) {
				throw std::runtime_error("datacenter Id can't be greater than 31 or less than 0");
			}

			workerid_ = workerid;
			datacenterid_ = datacenterid;
		}

		int64_t nextid()
		{
			//std::chrono::steady_clock  cannot decrease as physical time moves forward
			//改操作系统时间戳也不会导致减少
			auto timestamp = millsecond();
			if (last_timestamp_ == timestamp)
			{
				sequence_ = (sequence_ + 1) & SEQUENCE_MASK;
				if (sequence_ == 0)
				{
					timestamp = wait_next_millis(last_timestamp_);
				}
			}
			else
			{
				sequence_ = 0;
			}

			last_timestamp_ = timestamp;

			return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)//x位的时间序列，精确
				| (datacenterid_ << DATACENTER_ID_SHIFT)		  //x位数据中心标识，
				| (workerid_ << WORKER_ID_SHIFT)				 // x位机器标识 
				| sequence_;									//x位的计数序列号
		}

		static int64_t GetDataCenterId(int64_t id)
		{
			return (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
		}
		
	private:
		//获取时间戳 毫秒。 进程运行过程 改操作系统时间戳也不会导致时间戳 回退。（随着现时时间流逝每次调用都是增加的）
		int64_t millsecond() const noexcept
		{
			auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time_point_);
			return start_millsecond_ + diff.count();
		}

		int64_t wait_next_millis(int64_t last) const noexcept
		{
			auto timestamp = millsecond();
			while (timestamp <= last)
			{
				timestamp = millsecond();
			}
			return timestamp;
		}
	};



	/*
	提供弱引用任意类型。
	1）从对象获取的 weak_ptr 能保证弱引用特性。
	2）注意：从weak_ptr 获取的 shared_ptr 不能阻止对象释放。 
	3）和弱引用shared_ptr类似，不过对象定义不需要shared_ptr,只需要继承WeakPtr，这样定义的地方就可以保持原生写法。比如
		struct
		{
				ConcreteClass a; //不用shared_ptr<ConcreteClass> = ...;
		}

	Get函数获取成功那刻能保证对象有效， 后续的需要用户自己注意野指针了。
	使用例子：
	class ConcreteClass : public EnableWeakPtr<ConcreteClass>
	{
	public:

	};

	void f1()
	{
		ConcreteClass a;
		WeakPtr<ConcreteClass> wp = a;
		ConcreteClass* p = wp.Get();
		UNIT_ASSERT(p); //这里判断有值，能保证对象存在。后面使用就注意了
	}

	//错误使用例子：
	{
		ConcreteClass* p = wp.Get(); 
		UNIT_ASSERT(p); 
		someFun(); //里面释放了对象
		 ..... //p指针就野了。
	}
	*/

	template<class T>
	class WeakPtr {
		std::weak_ptr<T> m_ptr;
	public:
		WeakPtr()
		{
		}
		WeakPtr(const std::shared_ptr<T>& ptr)
			:m_ptr(ptr)
		{
		}
		WeakPtr(const WeakPtr& other)
		{
			m_ptr = other.m_ptr;
		}
		WeakPtr& operator=(const WeakPtr& other)
		{
			m_ptr = other.m_ptr;
			return *this;
		}
		bool operator==(const WeakPtr& other)
		{
			return m_ptr.lock().get() == other.m_ptr.lock().get();
		}

		T* Get()
		{
			std::shared_ptr<T> p = m_ptr.lock();
			return p.get();
		}

		const T* Get() const
		{
			std::shared_ptr<T> p = m_ptr.lock();
			return p.get();
		}
		
		void reset()
		{
			m_ptr.reset();
		}
	};

	template<class T>
	struct EnableWeakPtr {
		friend class WeakPtr<T>;
	private:
		std::shared_ptr<T> m_sharePtr;
		
	private:
		EnableWeakPtr(const EnableWeakPtr&)=delete; 
		EnableWeakPtr & operator=(EnableWeakPtr&&) = delete;//移动赋值函数
	public:
		EnableWeakPtr()
			:m_sharePtr((T*)this, [](T *) {})
		{

		}
		 //移动构造函数
		EnableWeakPtr(EnableWeakPtr&& other)
			:m_sharePtr((T*)this, [](T *) {})
		{
		}


		operator su::WeakPtr<T>() const
		{
			return m_sharePtr;
		}
		su::WeakPtr<T> GetWeakPtr() const
		{
			return m_sharePtr;
		}

		template<class BaseType>
		su::WeakPtr<BaseType> GetWeakPtr() const
		{
			std::shared_ptr<BaseType> sharedBase = m_sharePtr;
			return sharedBase;
		}
	};

} //end namespace su



//end file
